package ch.il07.converter.presentation;

import java.util.Arrays;
import java.util.HashMap;

/**
 * @author danney
 * Used for handling messages for the user correctly. Messages are stored in the session 
 * and messages which have not being rendered will automatically be restored in session.
 * Messages are only rendered once and no message will be lost.
 */
public class ErrorHandler {
	/**
	 * The key in session which is used to save the messages
	 */
	private static final String SESS_KEY = "ErrorHandlerMessages";
	
	private String template = "<div class='message ##K##'><p>##V##</p></div>";
	
	/**
	 * HashMap containing all messages
	 */
	private HashMap<String, HashMap<String,String>> messages;
	
	/**
	 * array containing all valid types
	 * (Can be used as CSS-classes)
	 */
	private String[] validTypes = { "error", "success", "warning", "info", "question" };

	/**
	 * Creates an instance of this class and gets all Messages in the session. 
	 * After that, the session is cleared to not get redundant data.
	 */
	public ErrorHandler() {
		HashMap<String, HashMap<String, String>> messages = getSessionData();
		
		if(null != messages) {
			this.messages = messages;
			clearSessionData();
		}else this.messages = new HashMap<String, HashMap<String, String>>();
	}

	/**
	 * @author danney
	 * On class destruction, we have to store the messages in the session.
	 */
	protected void finalize() throws Throwable {
		setupSession();
		super.finalize();
	}

	/**
	 * @author danney
	 * @param message error to add
	 * @return true if the message was added to the collection or false if type is not supported
	 * Adds the passed message to the <u>errors</u> collection.
	 */
	public boolean add(String message) { return add("error", message, null); }
	

	/**
	 * @author danney
	 * @param type if you want to set a specific message, enter the type of message here ['success', 'error', 'warning', 'info' or 'question']
	 * @param message error to add
	 * @return true if the message was added to the collection or false if type is not supported
	 * Adds the passed message to the <u>errors</u> collection.
	 */
	public boolean add(String type, String message) { return add(type, message, null); }
	
	/**
	 * @author danney
	 * @param message message to add
	 * @param type if you want to set a specific message, enter the type of message here ['success', 'error', 'warning', 'info' or 'question']
	 * @param key this is used only when you want to get this error anytime.
	 * @return true if the message was added to the collection or false if type is not supported
	 * Adds the passed message to the collection if the type is supported
	 */
	public boolean add(String type, String message, String key) {
		if(!Arrays.asList(validTypes).contains(type)) return false;
		if(null == messages.get(type)) messages.put(type, new HashMap<String, String>());
		if(null == key) key = "" + message.hashCode();
		messages.get(type).put(key, message);
		return true;
	}
	
	/**
	 * @author danney
	 * @return all errors with their view
	 * After execution, the message collection is cleared.
	 */
	public String getErrors() {
		StringBuilder errors = new StringBuilder();
		for(String type : messages.keySet()) {
			for(String msg : messages.get(type).values()) {
				errors.append(template.replace("##K##", type).replace("##V##", msg));
			}
		}
		clear();
		return errors.toString();
	}
	
	/**
	 * @author danney
	 * Use this method to get a specific message by passing the key
	 * @return a specific message or an empty string if message not found
	 */
	public String get(String key) {
		String error = messages.get("error").get(key);
		return null != error ? error : "";
	}
	
	/**
	 * @author danney
	 * Resets the collection.
	 */
	public void clear() {
		for(String type : validTypes) {
			if(null != messages.get(type))
				messages.get(type).clear();
		}
	}
	
	/**
	 * @author danney
	 * @return true if the collection has ANY message (of all types).
	 */
	public boolean hasEntries() {
		int count = 0;
		for(String type : messages.keySet())
			count += messages.get(type).size();
		return count > 0;
	}
	
	/**
	 * @author danney
	 * @return true if the collection contains messages of type error.
	 */
	public boolean hasErrors() {
		return null != messages.get("error") && messages.get("error").size() > 0;
	}
	
	/**
	 * @author danney
	 * Stores the messages in the session.
	 */
	public void setupSession() {
		setSessionData();
	}
	
	/**
	 * @author danney
	 * String representation of class
	 * @see ErrorHandler.getErrors()
	 */
	public String toString() {
		return getErrors();
	}

	@SuppressWarnings("unchecked")
	private HashMap<String, HashMap<String, String>> getSessionData() {
		return (HashMap<String, HashMap<String, String>>) Helper.getSession().getAttribute(SESS_KEY);
	}
	
	private void setSessionData() {
		Helper.getSession().setAttribute(SESS_KEY, messages);
	}
	
	private void clearSessionData() {
		Helper.getSession().setAttribute(SESS_KEY, null);
	}
}
